import datetime
import os
import sys
import time
import traceback
from contextvars import ContextVar

import aiomysql
import pickledb
from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from re_common.baselibrary.utils.baseip import BaseIp
from re_common.baselibrary.utils.core.requests_core import MsgCode
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.responses import PlainTextResponse
from starlette.staticfiles import StaticFiles

from app_include_router import set_include_router
from apps.sql_app.mmongodb import MongoDBClient, connect_to_mongo, close_mongo_connection, Coll
from apps.sql_app.redis import get_redis_pool
from settings import get_settings

sys.setrecursionlimit(100)

app = FastAPI(title="caiji project", openapi_url=f"/api/v1/openapi.json")
app.state.ip = str(BaseIp().GetLocalIPByPrefix("192.168"))
app.mount("/mdoc/static", StaticFiles(directory="static/docs"), name="static")

request_ctx_var = ContextVar("request", default=None)

# 设置apirouter的挂载
set_include_router(app)

sets = get_settings()

p_db = pickledb.load('fastapi.db', True)


@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    # 设置会话全局变量 相当于flask的g
    request.state.try_his = []
    request_ctx_var.set(request)
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    return response


@app.exception_handler(Exception)
async def all_exception_handler(request: Request, exc: Exception):
    """
    全局所有异常
    :param request:
    :param exc:
    :return:
    """
    from apps.file_uploadfile.models import ServerErrModel
    # print(request.__dict__)
    m = ServerErrModel().dict()
    m["ip_port"] = app.state.ip + ":" + str(app.state.port)
    m["client_ip"] = str(request.client.host)
    m["write_time"] = str(datetime.datetime.now())
    m["request_url"] = str(request.url)
    # https://github.com/tiangolo/fastapi/issues/1216 作者目前还没有解决这个bug
    # m.request_body = body.decode()
    m["request_body"] = str(request.state.mbody)
    m["err_type"] = str(sys.exc_info()[0])
    m["err_msg"] = str(sys.exc_info()[1])
    m["traceback_str"] = traceback.format_exc()
    save_dicts = ServerErrModel.parse_obj(m)
    m_result = Coll.get_server_exception()

    result = await m_result.insert_one(save_dicts.dict(by_alias=True))
    return PlainTextResponse(str(exc), status_code=MsgCode.SERVER_ERROR)


@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
    return PlainTextResponse(str(exc.detail), status_code=exc.status_code)


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
    """
    参数与给定的model不一致导致的错误
    :param request:
    :param exc:
    :return:
    """
    # body = await request.body()
    # body = body.decode()
    # print(body)
    return PlainTextResponse(str(exc), status_code=400)


# app.mount("/static", StaticFiles(directory="static"), name="static")

# uvicorn main:app --reload --host=0.0.0.0 --port=8000


@app.on_event("startup")
async def startup():
    """
    在所有 startup 事件处理程序完成 之前，您的应用程序将不会开始接收请求 。
    :return:
    """
    MongoDBClient.dbjsonhtml = await connect_to_mongo(
        sets.MONGODB_URI_HTMLJSON,
        sets.MONGODB_URI_HTMLJSON_DB)
    MongoDBClient.dbjsonlatest = await connect_to_mongo(
        sets.MONGODB_URI_HTMLJSONLATEST,
        sets.MONGODB_URI_HTMLJSONLATEST_DB)
    from apps.sql_app.database import proxys_sql_conn
    from apps.sql_app.database import down_product_conn
    await proxys_sql_conn.connect()
    await down_product_conn.connect()
    app.state.redis = await get_redis_pool()
    ppid = os.getppid()
    app.state.port = p_db.get(str(ppid) + "_port")
    app.state.tags = p_db.get(str(ppid) + "_tags")
    app.state.pool = await aiomysql.create_pool(host=sets.MYSQL_HOST_1,
                                                port=sets.MYSQL_PORT_1,
                                                user=sets.MYSQL_USER_1,
                                                password=sets.MYSQL_PWD_1,
                                                db=sets.MYSQL_DATEBASE_1)


@app.on_event("shutdown")
async def shutdown():
    """
    要添加应在应用程序关闭时运行的功能，请使用事件声明它 "shutdown"
    :return:
    """
    await close_mongo_connection(MongoDBClient.dbjsonhtml)
    from apps.sql_app.database import proxys_sql_conn
    from apps.sql_app.database import down_product_conn
    await proxys_sql_conn.disconnect()
    await down_product_conn.disconnect()
    app.state.redis.close()
    await app.state.redis.wait_closed()
    app.state.pool.close()
    await app.state.poll.wait_closed()
